 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.registry;

 import java.util.ArrayList ;
 import java.util.HashMap ;
 import java.util.Iterator ;
 import java.util.Map ;

 import org.eclipse.core.commands.contexts.Context;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IExtension;
 import org.eclipse.core.runtime.IExtensionPoint;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.dynamichelpers.ExtensionTracker;
 import org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler;
 import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
 import org.eclipse.ui.PlatformUI;
 import org.eclipse.ui.contexts.IContextService;
 import org.eclipse.ui.internal.WorkbenchPlugin;

 /**
  * The registry of action set extensions.
  */
 public class ActionSetRegistry implements IExtensionChangeHandler {
     
     /**
      * @since 3.1
      */
     private class ActionSetPartAssociation {
         /**
          * @param partId
          * @param actionSetId
          */
         public ActionSetPartAssociation(String partId, String actionSetId) {
             this.partId = partId;
             this.actionSetId = actionSetId;
         }
         
         
         String partId;
         String actionSetId;
     }
     
     private ArrayList children = new ArrayList ();

     private Map mapPartToActionSetIds = new HashMap ();
     
     private Map mapPartToActionSets = new HashMap ();

     private IContextService contextService;
     
     /**
      * Creates the action set registry.
      */
     public ActionSetRegistry() {
         contextService = (IContextService) PlatformUI
                 .getWorkbench().getService(IContextService.class);
         PlatformUI.getWorkbench().getExtensionTracker().registerHandler(
                 this,
                 ExtensionTracker
                         .createExtensionPointFilter(new IExtensionPoint[] {
                                 getActionSetExtensionPoint(),
                                 getActionSetPartAssociationExtensionPoint() }));
         readFromRegistry();
     }

     /**
      * Return the action set part association extension point.
      *
      * @return the action set part association extension point
      * @since 3.1
      */
     private IExtensionPoint getActionSetPartAssociationExtensionPoint() {
         return Platform
         .getExtensionRegistry().getExtensionPoint(
                 PlatformUI.PLUGIN_ID,
                 IWorkbenchRegistryConstants.PL_ACTION_SET_PART_ASSOCIATIONS);
     }

     /**
      * Return the action set extension point.
      *
      * @return the action set extension point
      * @since 3.1
      */
     private IExtensionPoint getActionSetExtensionPoint() {
         return Platform
                 .getExtensionRegistry().getExtensionPoint(
                         PlatformUI.PLUGIN_ID,
                         IWorkbenchRegistryConstants.PL_ACTION_SETS);
     }

     /**
      * Adds an action set.
      * @param desc
      */
     private void addActionSet(ActionSetDescriptor desc) {
         children.add(desc);
         Context actionSetContext = contextService.getContext(desc.getId());
         if (!actionSetContext.isDefined()) {
             actionSetContext.define(desc.getLabel(), desc.getDescription(),
                     "org.eclipse.ui.contexts.actionSet"); //$NON-NLS-1$
 }
     }

     /**
      * Remove the action set.
      *
      * @param desc
      */
     private void removeActionSet(IActionSetDescriptor desc) {
         Context actionSetContext = contextService.getContext(desc.getId());
         if (actionSetContext.isDefined()) {
             actionSetContext.undefine();
         }
         children.remove(desc);
     }

     /**
      * Adds an association between an action set an a part.
      */
     private Object addAssociation(String actionSetId, String partId) {
         // get the action set ids for this part
 ArrayList actionSets = (ArrayList ) mapPartToActionSetIds.get(partId);
         if (actionSets == null) {
             actionSets = new ArrayList ();
             mapPartToActionSetIds.put(partId, actionSets);
         }
         actionSets.add(actionSetId);
         
         ActionSetPartAssociation association = new ActionSetPartAssociation(partId, actionSetId);
         return association;
     }

     /**
      * Finds and returns the registered action set with the given id.
      *
      * @param id the action set id
      * @return the action set, or <code>null</code> if none
      * @see IActionSetDescriptor#getId
      */
     public IActionSetDescriptor findActionSet(String id) {
         Iterator i = children.iterator();
         while (i.hasNext()) {
             IActionSetDescriptor desc = (IActionSetDescriptor) i.next();
             if (desc.getId().equals(id)) {
                 return desc;
             }
         }
         return null;
     }

     /**
      * Returns a list of the action sets known to the workbench.
      *
      * @return a list of action sets
      */
     public IActionSetDescriptor[] getActionSets() {
         return (IActionSetDescriptor []) children.toArray(new IActionSetDescriptor [children.size()]);
     }

     /**
      * Returns a list of the action sets associated with the given part id.
      *
      * @param partId the part id
      * @return a list of action sets
      */
     public IActionSetDescriptor[] getActionSetsFor(String partId) {
         // check the resolved map first
 ArrayList actionSets = (ArrayList ) mapPartToActionSets.get(partId);
         if (actionSets != null) {
             return (IActionSetDescriptor[]) actionSets
                     .toArray(new IActionSetDescriptor[actionSets.size()]);
         }
         
         // get the action set ids for this part
 ArrayList actionSetIds = (ArrayList ) mapPartToActionSetIds.get(partId);
         if (actionSetIds == null) {
             return new IActionSetDescriptor[0];
         }
         
         // resolve to action sets
 actionSets = new ArrayList (actionSetIds.size());
         for (Iterator i = actionSetIds.iterator(); i.hasNext();) {
             String actionSetId = (String ) i.next();
             IActionSetDescriptor actionSet = findActionSet(actionSetId);
             if (actionSet != null) {
                 actionSets.add(actionSet);
             } else {
                WorkbenchPlugin.log("Unable to associate action set with part: " + //$NON-NLS-1$
 partId + ". Action set " + actionSetId + " not found."); //$NON-NLS-2$ //$NON-NLS-1$
 }
         }
         
         mapPartToActionSets.put(partId, actionSets);
         
         return (IActionSetDescriptor[]) actionSets
                 .toArray(new IActionSetDescriptor[actionSets.size()]);
     }

     /**
      * Reads the registry.
      */
     private void readFromRegistry() {
         IExtension[] extensions = getActionSetExtensionPoint().getExtensions();
         for (int i = 0; i < extensions.length; i++) {
             addActionSets(PlatformUI.getWorkbench().getExtensionTracker(),
                     extensions[i]);
         }

         extensions = getActionSetPartAssociationExtensionPoint()
                 .getExtensions();
         for (int i = 0; i < extensions.length; i++) {
             addActionSetPartAssociations(PlatformUI.getWorkbench()
                     .getExtensionTracker(), extensions[i]);
         }
     }

     /* (non-Javadoc)
      * @see org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler#addExtension(org.eclipse.core.runtime.dynamichelpers.IExtensionTracker, org.eclipse.core.runtime.IExtension)
      */
     public void addExtension(IExtensionTracker tracker, IExtension extension) {
         String extensionPointUniqueIdentifier = extension.getExtensionPointUniqueIdentifier();
         if (extensionPointUniqueIdentifier.equals(getActionSetExtensionPoint().getUniqueIdentifier())) {
             addActionSets(tracker, extension);
         }
         else if (extensionPointUniqueIdentifier.equals(getActionSetPartAssociationExtensionPoint().getUniqueIdentifier())){
             addActionSetPartAssociations(tracker, extension);
         }
     }

     /**
      * @param tracker
      * @param extension
      */
     private void addActionSetPartAssociations(IExtensionTracker tracker, IExtension extension) {
         IConfigurationElement [] elements = extension.getConfigurationElements();
         for (int i = 0; i < elements.length; i++) {
             IConfigurationElement element = elements[i];
             if (element.getName().equals(IWorkbenchRegistryConstants.TAG_ACTION_SET_PART_ASSOCIATION)) {
                 String actionSetId = element.getAttribute(IWorkbenchRegistryConstants.ATT_TARGET_ID);
                 IConfigurationElement[] children = element.getChildren();
                 for (int j = 0; j < children.length; j++) {
                     IConfigurationElement child = children[j];
                     if (child.getName().equals(IWorkbenchRegistryConstants.TAG_PART)) {
                         String partId = child.getAttribute(IWorkbenchRegistryConstants.ATT_ID);
                         if (partId != null) {
                             Object trackingObject = addAssociation(actionSetId, partId);
                             if (trackingObject != null) {
                                 tracker.registerObject(extension,
                                         trackingObject,
                                         IExtensionTracker.REF_STRONG);

                             }
                             
                         }
                     } else {
                         WorkbenchPlugin.log("Unable to process element: " + //$NON-NLS-1$
 child.getName() + " in action set part associations extension: " + //$NON-NLS-1$
 extension.getUniqueIdentifier());
                     }
                 }
             }
         }

         // TODO: optimize
 mapPartToActionSets.clear();
     }

     /**
      * @param tracker
      * @param extension
      */
     private void addActionSets(IExtensionTracker tracker, IExtension extension) {
         IConfigurationElement [] elements = extension.getConfigurationElements();
         for (int i = 0; i < elements.length; i++) {
             IConfigurationElement element = elements[i];
             if (element.getName().equals(IWorkbenchRegistryConstants.TAG_ACTION_SET)) {
                 try {
                     ActionSetDescriptor desc = new ActionSetDescriptor(element);
                     addActionSet(desc);
                     tracker.registerObject(extension, desc, IExtensionTracker.REF_WEAK);

                 } catch (CoreException e) {
                     // log an error since its not safe to open a dialog here
 WorkbenchPlugin
                             .log(
                                     "Unable to create action set descriptor.", e.getStatus());//$NON-NLS-1$
 }
             }
         }

         // TODO: optimize
 mapPartToActionSets.clear();
     }

     /* (non-Javadoc)
      * @see org.eclipse.core.runtime.dynamichelpers.IExtensionChangeHandler#removeExtension(org.eclipse.core.runtime.IExtension, java.lang.Object[])
      */
     public void removeExtension(IExtension extension, Object [] objects) {
         String extensionPointUniqueIdentifier = extension.getExtensionPointUniqueIdentifier();
         if (extensionPointUniqueIdentifier.equals(getActionSetExtensionPoint().getUniqueIdentifier())) {
             removeActionSets(objects);
         }
         else if (extensionPointUniqueIdentifier.equals(getActionSetPartAssociationExtensionPoint().getUniqueIdentifier())){
             removeActionSetPartAssociations(objects);
         }
     }

     /**
      * @param objects
      */
     private void removeActionSetPartAssociations(Object [] objects) {
         for (int i = 0; i < objects.length; i++) {
             Object object = objects[i];
             if (object instanceof ActionSetPartAssociation) {
                 ActionSetPartAssociation association = (ActionSetPartAssociation) object;
                 String actionSetId = association.actionSetId;
                 ArrayList actionSets = (ArrayList ) mapPartToActionSetIds.get(association.partId);
                 if (actionSets == null) {
                     return;
                 }
                 actionSets.remove(actionSetId);
                 if (actionSets.isEmpty()) {
                     mapPartToActionSetIds.remove(association.partId);
                 }
             }
         }
         // TODO: optimize
 mapPartToActionSets.clear();
         
     }

     /**
      * @param objects
      */
     private void removeActionSets(Object [] objects) {
         for (int i = 0; i < objects.length; i++) {
             Object object = objects[i];
             if (object instanceof IActionSetDescriptor) {
                 IActionSetDescriptor desc = (IActionSetDescriptor) object;
                 removeActionSet(desc);

                 // now clean up the part associations
 // TODO: this is expensive. We should consider another map from
 // actionsets
 // to parts.
 for (Iterator j = mapPartToActionSetIds.values().iterator(); j
                         .hasNext();) {
                     ArrayList list = (ArrayList ) j.next();
                     list.remove(desc.getId());
                     if (list.isEmpty()) {
                         j.remove();
                     }
                 }
             }
         }
         // TODO: optimize
 mapPartToActionSets.clear();
     }
 }

